package com.cap.bts.framework.auth.filter;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.naming.ServiceUnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.cap.bts.framework.auth.login.UserAutoLogin;
import com.cap.bts.framework.auth.util.AuthHelper;
import com.cap.bts.framework.auth.util.SSOConfigurations;
import com.cap.bts.framework.auth.util.WebUtils;
import com.cap.bts.framework.common.exception.AppException;
import com.cap.bts.framework.common.util.ApplicationContextUtil;
import com.michelin.aad.graph.api.model.AzureADUser;
import com.michelin.aad.graph.api.services.impl.UserServicesImpl;
import com.microsoft.aad.adal4j.AuthenticationContext;
import com.microsoft.aad.adal4j.AuthenticationResult;
import com.microsoft.aad.adal4j.ClientCredential;
import com.nimbusds.oauth2.sdk.AuthorizationCode;
import com.nimbusds.openid.connect.sdk.AuthenticationErrorResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationResponse;
import com.nimbusds.openid.connect.sdk.AuthenticationResponseParser;
import com.nimbusds.openid.connect.sdk.AuthenticationSuccessResponse;

public class SSOAuthInterceptor extends HandlerInterceptorAdapter {
	private static Logger logger = Logger.getLogger(SSOAuthInterceptor.class);

	private String clientId = SSOConfigurations
			.getConfig("dealer.portal.clientId");
	private String clientSecret = SSOConfigurations
			.getConfig("dealer.portal.clientSecret");
	private String tenant = SSOConfigurations.getConfig("dealer.portal.tenant");
	private String authority = SSOConfigurations
			.getConfig("dealer.portal.authority");
	private String graphApi = SSOConfigurations
			.getConfig("dealer.portal.graphApi");
	private String graphApiEncode = SSOConfigurations
			.getConfig("dealer.portal.graphApiEncode");
	public static final String SSO_LOGIN_ORGS="sso_login_orgs";
	
	private String portalurl = SSOConfigurations.getConfig("dealer.portal.url");

	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		super.afterCompletion(request, response, handler, ex);
	}

	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		// TODO Auto-generated method stub
		// return super.preHandle(request, response, handler);
		logger.info("in sso ...");

		String ssoURI = request.getRequestURI();

		if (ssoURI.endsWith("sso/login")) {
			try {
				// 获取当前URL，供Azure AD应用程序 redirect使用
				String currentUri = SSOConfigurations.getConfig("dealer.portal.sso.login.url");
				
				String fullUrl = currentUri
						+ (request.getQueryString() != null ? "?"
								+ request.getQueryString() : "");
				
				
				
				// 如果Session中未包含登入信息
				if (!AuthHelper.isAuthenticated(request)) {
					logger.info("start login..");
					// 判断是否从Azure AD登入界面重定向回来的请求，是否包含对应的返回参数
					if (AuthHelper.containsAuthenticationData(request)) {
						// 将返回参数放至params对象中
						Map<String, String> params = new HashMap<String, String>();
						for (Object key : request.getParameterMap().keySet()) {
							String[] arrays = (String[]) request
									.getParameterMap().get(key);
							params.put((String) key, arrays[0]);
							logger.info("param:" + key + " value:"
									+ request.getParameterMap().get(key));
						}

						// 验证idc_token等信息有效性
						AuthenticationResponse authResponse = AuthenticationResponseParser
								.parse(new URI(fullUrl), params);
						logger.info("validat idc_token...");
						// 如果authResponse为AuthenticationSuccessResponse
						// 成功响应实例，则说明可以创建用户 Session信息
						if (AuthHelper.isAuthenticationSuccessful(authResponse)) {
							AuthenticationSuccessResponse oidcResponse = (AuthenticationSuccessResponse) authResponse;
							logger.info("validate success...");
							// 获取AccessToken信息

							AuthenticationResult result = getAccessToken(
									oidcResponse.getAuthorizationCode(),
									currentUri);
							logger.info("get AuthenticationResult success! tokenis:"
									+ result.getAccessToken());
							createSessionPrincipal(request, result);
							// Session信息创建成功后回到首页，由AutoLogin Filter完成登入操作

							String userCode =getDealerCodeByAuthenticationResult(result,request,response);
							
							if(userCode!=null){
								if(userCode.startsWith("redrect:")){
									response.sendRedirect(userCode.substring(8));
									return false;
								}
							}
							
						} else {
							AuthenticationErrorResponse oidcResponse = (AuthenticationErrorResponse) authResponse;
							throw new Exception(String.format(
									"Request for auth code failed: %s - %s",
									oidcResponse.getErrorObject().getCode(),
									oidcResponse.getErrorObject()
											.getDescription()));
						}

					} else {
						// 如果未登入，并且不包含必须参数，则重定向至微软件的Azure AD登入界面
						response.setStatus(302);
						
						String redrectUrl = getRedirectUrl(currentUri);
						logger.info("SSO URL:" + redrectUrl);
						response.sendRedirect(redrectUrl);
						return false;
					}

				} else {
					// if authenticated, how to check for valid session?
					// REFRESH TOKEN
					logger.info("SSO Error: maybe token expired!");
					
					AuthenticationResult result =(AuthenticationResult) request.getSession().getAttribute(
							AuthHelper.PRINCIPAL_SESSION_NAME);
					String userCode =getDealerCodeByAuthenticationResult(result,request,response);
					
					if(userCode!=null){
						if(userCode.startsWith("redrect:")){
							response.sendRedirect(userCode.substring(8));
							return false;
						}
					}
					
					response.sendRedirect("/crm/buildMenu");
					return false;
				}
			} catch (Throwable exc) {
				response.setStatus(500);
				exc.printStackTrace();
				logger.error("SSO Error:" + exc.getMessage());
			}
		}
		return true;
	}
	
	private String getDealerCodeByAuthenticationResult(AuthenticationResult result,HttpServletRequest request,HttpServletResponse response) throws AppException{
		

		UserAutoLogin userAutoLogin = (UserAutoLogin) ApplicationContextUtil
				.getApplicationContext().getBean("userAutoLogin");
		UserServicesImpl userService = new UserServicesImpl();
		
		// 通过Access Token获取用户信息,
		AzureADUser user = userService.getCurrentUserInfo(result
				.getAccessToken());
		String userCode = null;
		
		if ("U200".equals(user.getMessageCode())) {
			String orgnizations = user.getOrganizations();
			String roles = user.getRoles();
			
			
			logger.info("orgnizations..."+orgnizations);
			
			
			if(StringUtils.isNotEmpty(orgnizations)){
				String[] orgs = orgnizations.split(",");
				
				if(orgs!=null && orgs.length>0){
					//当前组织(Dealer)只有一个的情况
					if(orgs.length==1){
						userCode=orgs[0];
					//当组织(Dealer)超过一个时
					}else if(orgs.length > 1){
						if(request.getParameter("selectedOrgID")!=null){
							String selectedOrgId = request.getParameter("selectedOrgID");
							userCode = selectedOrgId;
						}else{
							request.getSession().setAttribute(SSOAuthInterceptor.SSO_LOGIN_ORGS, orgnizations);
							return "redrect:/crm/login/toSelectedOrgs";
						}
					}
					logger.info("userCode:--"+userCode);
				}
			}
			
			//根据角色更新userCode
			userCode =updateUserCodeByRoles(roles,userCode);
			
		}
		
		if(userCode==null){
			logger.info("SSO: user not allow to come here!");
			return "redrect:"+portalurl;//https://dp.michelin.com.cn/
		}else{
			//应用程序自动登录
			logger.info("user code:"+userCode);
			boolean isSelectOrgs = StringUtils.endsWith(request.getHeader("referer"), "login/toSelectedOrgs");
			userAutoLogin.login(request, response, userCode, isSelectOrgs);
			
			request.getSession().setAttribute("userObjectId", user.getObjectId());
			request.getSession(true).setAttribute("IS_SSO_LOGIN", "Y");//如果通过 sso登录，标记为Y，供检验是否需要校验当前用户登录状态
			
			return "redrect:/crm/buildMenu";
		
		}
//		return userCode;
	}
	
	
	private String updateUserCodeByRoles(String roles,String userCode){
		
		if(StringUtils.isNotEmpty(roles)){
			String[] rols = roles.split(",");
			boolean adminflag=false;//店长flag
			boolean flag=false;//前台出纳flag
			if(rols!=null && rols.length>0){
				String allowRoles=SSOConfigurations.getConfig("dealer.portal.sso.allow.admin.roles");
				String[] allowRolesArray = allowRoles.split(",");
				
				Map<String,String> rolesMap = new HashMap<String,String>();
				for(String role:allowRolesArray){
					rolesMap.put(role, "1");
				}
				for(String role:rols){
					if("1".equals(rolesMap.get(role))){
//						userCode=userCode+"_admin";
//						break;
						adminflag=true;
					}else if("前台出纳".equals(role)){
//						userCode=userCode;
//						break;
						flag=true;
					}
				}
				if(adminflag){//用户是店长
					userCode=userCode+"_admin";
				}
				if(flag){//用户是前台出纳
					userCode=userCode;
				}
				if(adminflag&flag){//用户即是店长又是出纳
					userCode=userCode+"_admin";
				}
				if(adminflag==false&flag==false){//都不是
					userCode=null;
				}
				
				
			}
		}
		
		return userCode;
	}
	
	private AuthenticationResult getAccessToken(
			AuthorizationCode authorizationCode, String currentUri)
			throws Throwable {
		String authCode = authorizationCode.getValue();
		ClientCredential credential = new ClientCredential(clientId,
				clientSecret);
		AuthenticationContext context = null;
		AuthenticationResult result = null;
		ExecutorService service = null;
		try {
			service = Executors.newFixedThreadPool(1);
			context = new AuthenticationContext(authority + tenant + "/", true,
					service);
			Future<AuthenticationResult> future = context
					.acquireTokenByAuthorizationCode(authCode, new URI(
							currentUri), credential, null);
			result = future.get();
		} catch (ExecutionException e) {
			throw e.getCause();
		} finally {
			service.shutdown();
		}

		if (result == null) {
			throw new ServiceUnavailableException(
					"authentication result was null");
		}
		return result;
	}

	private void createSessionPrincipal(HttpServletRequest httpRequest,
			AuthenticationResult result) throws Exception {
		httpRequest.getSession().setAttribute(
				AuthHelper.PRINCIPAL_SESSION_NAME, result);
	}

	/**
	 * 重定向至 Azure AD登入界面
	 * 
	 * @param currentUri
	 *            当前URL，需要提前在Azure AD中返回列表中配制
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private String getRedirectUrl(String currentUri)
			throws UnsupportedEncodingException {
		String redirectUrl = authority
				+ this.tenant
				+ "/oauth2/authorize?response_type=code%20id_token&scope=openid&response_mode=form_post&redirect_uri="
				+ URLEncoder.encode(currentUri, "UTF-8") + "&client_id="
				+ clientId + "&resource=" + graphApiEncode + "&nonce="
				+ UUID.randomUUID();// + "&site_id="+sitesId
		System.out.println(redirectUrl);
		return redirectUrl;
	}
}
